package com.lagou.edu.factory.impl;

import com.lagou.edu.annotation.*;
import com.lagou.edu.config.BeanDefinition;
import com.lagou.edu.config.PropertyValue;
import com.lagou.edu.factory.*;
import com.lagou.edu.io.ResourceLoader;
import com.lagou.edu.io.impl.DefaultResourceLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName: DefaultFactory
 * @Auther: Jerry
 * @Date: 2020/7/7 22:44
 * @Desctiption: beanFactory实现
 * @Version: 1.0
 */
public class DefaultFactory implements BeanFactory {

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 单例池
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 二级缓存
     */
    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    /**
     * 三级缓存
     */
    private Map<String, Object> singletonFactories = new ConcurrentHashMap<>();

    private static Properties properties = new Properties();

    /** 需要扫描的包名 */
    private static final String BASE_PACKAGE = "base-package";

    /** 需要排除的包名 */
    private static final String EXCLUDE_PACKAGE = "exclude-package";

    /**
     * 配置文件路径
     */
    private String configLocation;

    public String getConfigLocation() {
        return configLocation;
    }

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    public DefaultFactory(String configLocation) throws DocumentException {
        this.configLocation = configLocation;
        // 加载并解析xml资源文件
        loadConfigLocation(configLocation);
        // 初始化
        refresh();
    }

    /**
     * 加载并解析xml资源文件
     * @param classPath 类路径
     * @throws DocumentException 文件异常
     */
    private void loadConfigLocation(String classPath) throws DocumentException {
        // 加载主配置文件
        InputStream inputStream = DefaultFactory.class.getClassLoader().getResourceAsStream(classPath);
        Document read = new SAXReader().read(inputStream);
        // 获取根节点
        Element root = read.getRootElement();
        // 获取包扫描配置节点
        List<Element> componentScanList = root.selectNodes("//component-scan");
        for (Element element : componentScanList) {
            // 获取包扫描配置
            String basePackage = element.attributeValue("base-package");

            // 排除包扫描配置
            String exclude = element.attributeValue("exclude-package");
            properties.setProperty(BASE_PACKAGE, basePackage);
            properties.setProperty(EXCLUDE_PACKAGE, exclude);
        }
    }

    public void refresh() {
        try {
            String basePackage = properties.getProperty(BASE_PACKAGE);
            String excludePackage = properties.getProperty(EXCLUDE_PACKAGE);

            ResourceLoader reader = new DefaultResourceLoader();
            List<BeanDefinition> beanDefinitions = reader.reader(basePackage, excludePackage);
            // 将bean注册为beanDefinition对象
            registerBeanDefinition(beanDefinitions);
            // 注册bean
            registerBean(beanDefinitions);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    /**
     * 注册的Bean定义
     * @param beanDefinitions bean的定义
     */
    private void registerBeanDefinition(List<BeanDefinition> beanDefinitions) {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            this.beanDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition);
        }
    }

    /**
     * 注册bean
     * @param beanDefinitions bean的定义
     * @throws IllegalAccessException 非法访问异常
     * @throws NoSuchFieldException 没有这样的磁场异常
     * @throws InstantiationException 实例化异常
     */
    private void registerBean(List<BeanDefinition> beanDefinitions) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            getBean(beanDefinition.getBeanName());
        }
    }

    /**
     * 从bean工厂获取bean
     * @param beanName beanName
     * @return {@link Object}
     */
    @Override
    public Object getBean(String beanName) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        return doGetBean(beanName);
    }

    private Object doGetBean(String beanName) throws IllegalAccessException, NoSuchFieldException,
            InstantiationException {
        //首先从缓存中读取
        Object singleton = getSingleton(beanName);
        if(null != singleton){
            return singleton;
        }
        singleton = getSingleton(beanName, () -> {
            return doCreateBean(beanName);
        });
        return singleton;
    }

    /**
     * 从单例池获取bean对象
     * @param beanName bean的名字
     * @return {@link Object}
     */
    private Object getSingleton(String beanName) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.singletonFactories.get(beanName);
                    if (singletonObject != null) {
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        synchronized (this.singletonObjects) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //从二级缓存中取出bean并将bean放入单例池中
                singletonObject = singletonFactory.getObject();
                addSingleton(beanName, singletonObject);
            }
            return singletonObject;
        }
    }

    /**
     * 添加bean到单例池
     * @param beanName bean的名字
     * @param singletonObject 单例对象
     */
    private void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.put(beanName, singletonObject);
            this.earlySingletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
        }
    }

    private Object doCreateBean(String beanName) throws InstantiationException, IllegalAccessException,
            NoSuchFieldException {
        // 创建 Bean 实例，仅仅调用构造方法，但是尚未设置属性
        Object exposedBean = instanceBean(beanName);
        // 放入三级缓存
        addSingletonFactory(beanName, exposedBean);
        // Bean属性填充
        populateBean(beanName, exposedBean);
        // 调用初始化方法
        exposedBean = initializeBean(beanName, exposedBean);
        return exposedBean;
    }

    /**
     * Bean实例化
     * @param beanName bean的名字
     * @return {@link Object}* @throws IllegalAccessException 非法访问异常
     * @throws InstantiationException 实例化异常
     */
    private Object instanceBean(String beanName) throws IllegalAccessException, InstantiationException {
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        if (beanDefinition != null) {
            Class<?> beanClass = beanDefinition.getBeanClass();
            return beanClass.newInstance();
        }
        return null;
    }

    /**
     * 将bean添加到bean工厂
     * @param beanName bean的名字
     * @param exposedBean 暴露的bean
     */
    private void addSingletonFactory(String beanName, Object exposedBean) {
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, exposedBean);
            }
        }
    }

    /**
     * 填充Bean
     * @param beanName bean的名字
     * @param exposedBean 暴露的bean
     * @throws NoSuchFieldException 没有这样的磁场异常
     * @throws IllegalAccessException 非法访问异常
     * @throws InstantiationException 实例化异常
     */
    private void populateBean(String beanName, Object exposedBean) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        List<PropertyValue> propertyValueList = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValueList) {
            if (propertyValue.getRefFlag()) {
                // 根据依赖的beanName获取对应的bean
                Object bean = this.getBean(propertyValue.getBeanName());
                Field field = exposedBean.getClass().getDeclaredField(propertyValue.getName());
                // 暴力访问
                field.setAccessible(true);
                // 建立依赖关系
                field.set(exposedBean, bean);
            }
        }
    }

    /**
     * bean扩展
     * @param beanName
     * @param exposedBean
     * @return {@link Object}
     */
    private Object initializeBean(String beanName, Object exposedBean) {
        BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        if (beanDefinition != null) {
            Class<?> beanClass = beanDefinition.getBeanClass();
            if (isTransactionalAnno(beanClass)) {
                // 返回代理类：添加事务逻辑
                return ProxyFactory.newInstance().getProxyInstance(exposedBean);
            }
        }
        return exposedBean;
    }

    /**
     * 是否包含事务注解
     * @param beanClass bean类
     * @return boolean
     */
    private boolean isTransactionalAnno(Class<?> beanClass) {
        boolean flag = false;
        MiniTransactional transactional = beanClass.getAnnotation(MiniTransactional.class);
        if (transactional != null) {
            flag = true;
        }

        Method[] declaredMethods = beanClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            // 获取有MiniTransactional注解的字段
            MiniTransactional miniTransactional = method.getAnnotation(MiniTransactional.class);
            if (miniTransactional != null) {
                flag = true;
            }
        }
        return flag;
    }

}
